package com.ccc.boot.security.config;

import com.ccc.boot.constant.CommonConstant;
import com.ccc.boot.security.constant.AuthConstant;
import com.ccc.boot.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * 自定义Spring Security核心配置类——认证服务器配置，让security来保护我们的API
 *
 * @author superC
 * @version 0.1.0
 * @date 2022/3/23
 */

@Configuration
/**
 * EnableWebSecurity：这个注解的意思是这个类是Spring Security的配置类
 */
@EnableWebSecurity
/**
 * EnableGlobalMethodSecurity(securedEnabled = true)
 * 开启权限控制的注解支持；
 *      prePostEnabled 表示SpringSecurity内部的权限控制注解开关。
 *      securedEnabled 属性支持@Secured注解，支持角色级别的权限控制。
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        // 设置默认的加密方式，使用BCrypt进行密码的hash
        return new BCryptPasswordEncoder();
    }

    /**
     * 用来配置用户签名服务，定义用户、密码、角色
     *
     * @param auth 签名管理器构造器，用于构建用户具体权限控制
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                // 设置UserDetailsService，使用自定义认证与授权
                .userDetailsService(userService)
                // 使用BCrypt进行密码的hash
                .passwordEncoder(passwordEncoder());
    }

    /**
     * 配置SpringSecurity相关信息
     *
     * @param http
     * @throws Exception
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                // 关闭csrf跨站点请求伪造（由于使用的是JWT，我们这里不需要csr）
                .csrf().disable()

                //  表示当前限定下的请求，允许访问
                .authorizeRequests()
                .antMatchers(AuthConstant.IGNORE_URIS_LOGIN).permitAll()

                // 限定指定路径下的请求需要赋予指定角色才能进行访问
                .antMatchers(CommonConstant.SYS_URIS).hasAuthority(AuthConstant.ADMIN)

                // 表示除上面外的其它所有请求资源，全部需要鉴权认证（认证通过后才可访问）
                .and()
                .authorizeRequests()
                .anyRequest().authenticated()

                .and()
                .exceptionHandling()
                .accessDeniedHandler(new MyAccessDeniedHandler())

                // 基于token，所以不需要session（因为使用了JWT，表明了我们的应用是一个前后端分离的应用，所以我们可以开启STATELESS禁止使用session）
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)

        ;

        // 添加JWT身份认证的filter。将我们的自定义jwtAuthenticationTokenFilter，加载到UsernamePasswordAuthenticationFilter的前面
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

}